package com.recharge.service.httpInterFace;

import com.alibaba.fastjson.JSONObject;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.AESUtil;
import com.recharge.common.util.Constant;
import com.recharge.common.util.MD5Util;
import com.recharge.common.util.TemplateUtils;
import com.recharge.common.util.http.HttpClientUtil;
import com.recharge.domain.query.OrderStatusQueryDO;
import com.recharge.domain.vo.GoodsChannelDO;
import com.recharge.domain.vo.OrderDO;
import com.recharge.domain.vo.ProviderApiDO;
import com.recharge.domain.vo.ProviderCallbackDO;
import com.recharge.service.OrderService;
import com.recharge.service.ProviderApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <h1>上游请求(星代充)</h1>
 */
@Service
public class XinDaiChongHTTPImpl implements ProviderHTTPService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    ProviderApiService providerApiService;

    @Autowired
    OrderService orderService;

    @Override
    public ResultDO commitOrder(GoodsChannelDO domain) {
        // 判断订单流水是否为空
        if (TemplateUtils.isEmpty(domain.getProviderSerialNumber())) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单流水号为空 %s", domain.toString())
                    , "订单号为空"
                    , null);
        }
        // 添加存根
        ProviderCallbackDO callbackDO = new ProviderCallbackDO();
        callbackDO.setSerialNumber(domain.getProviderSerialNumber());
        callbackDO.setProviderId(domain.getProviderId());
        callbackDO.setOrderNo(domain.getOrderNo());
        callbackDO.setStatus(Constant.CALL_BACK_PROCESS);
        ResultDO result = providerApiService.addCallback(callbackDO);
        if (!result.isSuccess()) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单%s添加凭证数据失败", domain.getProviderSerialNumber())
                    , "添加回调存根信息失败"
                    , null);
        }

        Map<String, String> signInfo = new HashMap<>();

        //请求金额
        //商户编号
        signInfo.put("customerNo", String.valueOf(domain.getUserId()));
        //商户订单号
        signInfo.put("outTradeNo", domain.getProviderSerialNumber());
        //代充号码
        signInfo.put("rechargeNo", domain.getRechargeNumber());
        //产品类型
        switch (domain.getServiceProvider()) {
            case 1:
                signInfo.put("productType", "MOBILE");
                break;
            case 2:
                signInfo.put("productType", "UNICOM");
                break;
            case 3:
                signInfo.put("productType", "TELECOM");
                break;
            default:
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                        , String.format("订单提交 >> 订单%s未查询到运营商", domain.getProviderSerialNumber())
                        , "未查询到运营商"
                        , null);
        }

        //充值卡序号(密文)
        signInfo.put("cardSn", AESUtil.encrypt(domain.getCardNo(), domain.getPrivateKey()));
        //充值卡密码(密文)
        signInfo.put("cardPwd", AESUtil.encrypt(domain.getCardPassword(), domain.getPrivateKey()));
        //充值金额(元)
        signInfo.put("amount", String.valueOf(domain.getAmount()/1000));
        //回调地址(选填)
        signInfo.put("notifyUrl", "");
        //充值号码所在地(选填)
        signInfo.put("area", "");
        //时间戳
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
        signInfo.put("timestamp", formatter.format(new Date()));
        //签名
        signInfo.put("sign", MD5Util.md5(signInfo, domain.getPrivateKey()).toUpperCase());

        // 发送请求
        logger.warn(String.format("订单提交 >> 发送GET请求(订单号%s): %s?%s", domain.getProviderSerialNumber(), domain.getRequestUrl(), signInfo.toString()));
        String sendResult = HttpClientUtil.doGet(domain.getRequestUrl(), signInfo);
        switch (sendResult) {
            case "500":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求失败", domain.getProviderSerialNumber())
                        , "HTTP请求失败"
                        , sendResult);
            case "504":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求超时", domain.getProviderSerialNumber())
                        , "HTTP请求超时"
                        , sendResult);
            case "":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求没有返回信息", domain.getProviderSerialNumber())
                        , "订单提交没有返回信息"
                        , sendResult);
        }
        logger.warn(String.format("订单提交 >> 请求返回信息: 订单号%s -> %s", domain.getProviderSerialNumber(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("订单提交 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 提交订单没有返回信息", domain.getProviderSerialNumber())
                    , "订单没有返回信息"
                    , sendResult);
        }

        String code = null;
        String desc = "";
        try {
            code = resultJson.get("returnCode").toString();
            desc = resultJson.get("returnMsg").toString();
        } catch (Exception e) {
            logger.debug(String.format("订单提交 >> 获取返回信息失败: 订单号%s -> %s", domain.getProviderSerialNumber(), sendResult));
        }
        if (code == null) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 没有获取到返回状态码", domain.getProviderSerialNumber())
                    , "没有获取到返回状态码"
                    , sendResult);
        }

        // 状态码: 提交失败
        String[] array = {"0001", "0002", "0003", "0004", "0007", "0008", "0009", "0010", "0011", "0012", "0014", "0016", "0017", "error", "M-9999"};

        // 状态码: 未确认
        String[] array2 = {"0006"};

        if ("ok".equals(code)) { //成功
            logger.debug(String.format("订单提交 >> 订单 %s -> 订单提交成功|%s", domain.getProviderSerialNumber(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.BUSINESS_RECHARGE_RETURN_STATUS_SUCCESS);
            result.setModel(ResultDO.SECOND_MODEL_KEY, "");
            return result;
        } else if ("0005".equals(code)) { //卡密无效
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_CARD_INVALID
                    , String.format("订单提交 >> 订单 %s -> 订单卡密无效|%s", domain.getProviderSerialNumber(), desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else if (Arrays.asList(array).contains(code)) {//失败
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单 %s -> 订单提交失败|%s", domain.getProviderSerialNumber(), desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else if (Arrays.asList(array2).contains(code)) {//未确认
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单 %s -> 订单未确认|%s", domain.getProviderSerialNumber(), desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else {//未知
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单 %s -> 订单未知|%s", domain.getProviderSerialNumber(), desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        }

    }

    @Override
    public ResultDO backOrder(ProviderApiDO domain, String paramStr, String bodyStr) {
        ResultDO result = new ResultSupport();
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(paramStr);
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析后数据: " + jsonObject.toJSONString());
        } catch (Exception e) {
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析失败:" + paramStr);
        }
        if (jsonObject == null) {
            logger.debug("订单异步回调 >> 收到异步回调,但是没有回调信息");
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        //商户编号
        String customerNo = jsonObject.getString("customerNo");
        //平台流水号
        String outTradeNo = jsonObject.getString("outTradeNo");
        //上游流水号
        String orderNo = jsonObject.getString("orderNo");
        //充值号码
        String rechargeNo = jsonObject.getString("rechargeNo");
        //充值金额(元)
        String successAmount = jsonObject.getString("successAmount");
        //代充手续费
        String feeAmount = jsonObject.getString("feeAmount");
        //完成时间
        String finishTime = jsonObject.getString("finishTime");
        //充值状态
        String status = jsonObject.getString("status");
        //失败错误码
        String failedCode = jsonObject.getString("failedCode");
        //校验签名
        String sign = jsonObject.getString("sign");

        Map<String, String> map = new HashMap<>();
        map.put("customerNo", customerNo);
        map.put("outTradeNo", outTradeNo);
        map.put("orderNo", orderNo);
        map.put("rechargeNo", rechargeNo);
        map.put("successAmount", successAmount);
        map.put("feeAmount", feeAmount);
        map.put("finishTime", finishTime);
        map.put("status", status);
        map.put("failedCode", failedCode);
        //本地加密签名
        String localSign = MD5Util.md5(map, domain.getPrivateKey()).toUpperCase();

        //验签
        if (!sign.equals(localSign)) {
            logger.debug(String.format("订单异步回调 >> 订单:%s 签名校验不一致 {本地签名:%s 返回签名:%s }", outTradeNo, localSign, sign));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        //判断订单状态
        if (status.equals("SUCCESS")) {
            try {
                result = orderService.getByProviderOrderSerialNo(outTradeNo);
            } catch (Exception e) {
                logger.debug("订单异步回调 >> " + outTradeNo + "未获取到订单信息 | " + e.getMessage());
                result.setModel(ResultDO.FIRST_MODEL_KEY, "FAIL");
                return result;
            }
            if (result.getModel(ResultDO.FIRST_MODEL_KEY) == null) {
                logger.debug("订单异步回调 >> " + outTradeNo + "未获取到订单信息");
                result.setModel(ResultDO.FIRST_MODEL_KEY, "FAIL");
                return result;
            }

            OrderDO orderDO = (OrderDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
            if (successAmount.equals(String.valueOf(orderDO.getAmount()))) {
                logger.debug(String.format("订单异步回调 >> %s 成功 status: %s", outTradeNo, status));
                result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_SUCCESS);
            } else {
                logger.debug(String.format("订单异步回调 >> %s 成功 status: %s (金额异常: 返回金额 %s , 本地订单金额 %s)", outTradeNo, status, successAmount, orderDO.getAmount()));
                result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_SUCCESS);
            }
        } else if (status.equals("FAILED")) {
//            String[] fails = {"F001", "F002", "F003", "F004", "F005", "F006", "F007", "F008", "F009", "F010"};
//            if (Arrays.asList(fails).contains(failedCode)){
//                logger.debug(String.format("订单异步返回:%s 失败 status: %s, failedCode: %s", outTradeNo, status, failedCode));
//                result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_FAIL);
//            }else {
//                logger.debug(String.format("订单异步返回:%s 失败 status: %s, failedCode: %s", outTradeNo, status, failedCode));
//                result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_FAIL);
//            }
            logger.debug(String.format("订单异步回调 >> %s 失败 status: %s, failedCode: %s", outTradeNo, status, failedCode));
            result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_FAIL);
        } else {
            logger.debug(String.format("订单异步回调 >> %s 未确认 status: %s, failedCode: %s", outTradeNo, status, failedCode));
            result = providerApiService.updateCallback(outTradeNo, "", orderNo, Constant.CALL_BACK_NUKNOW);
        }
        if (!result.isSuccess()) {
            logger.debug(String.format("订单异步回调 >> %s 修改订单状态失败", outTradeNo));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "FAIL");
            return result;
        }
        result.setModel(ResultDO.FIRST_MODEL_KEY, "SUCCESS");
        return result;

    }

    @Override
    public ResultDO queryOrder(OrderStatusQueryDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null) {
            logger.debug("查询订单 >> 密钥为空");
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "密钥为空");
            return result;
        }
        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("outTradeNo", domain.getSerialNo());
        requestParamMap.put("customerNo", domain.getUserId());
        requestParamMap.put("timestamp", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis()));
        String sign = "";
        try {
            sign = MD5Util.md5(requestParamMap, domain.getPrivateKey());
        } catch (Exception e) {
            logger.debug("查询订单 >> 订单(" + domain.getSerialNo() + ") 签名加密失败" + e.getMessage());
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "签名加密失败");
            return result;
        }
        requestParamMap.put("sign", sign);

        // 发送请求
        logger.debug(String.format("查询订单 >> 发送GET请求(订单号%s): url %s , param %s", domain.getSerialNo(), domain.getOrderFindUrl(), requestParamMap));
        String sendResult = HttpClientUtil.doGet(domain.getOrderFindUrl(), requestParamMap);
        logger.debug(String.format("查询订单 >> 请求返回信息: 订单号%s -> %s", domain.getSerialNo(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = TemplateUtils.xml2Json(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 请求返回结果解析XML失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            logger.debug(String.format("查询订单 >> 订单(%s)未获取到返回结果", domain.getSerialNo()));
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回结果");
            return result;
        }

        String code = null;
        String status = "";
        String desc = "";
        try {
            code = resultJson.get("returnCode").toString();
            desc = resultJson.get("returnMsg").toString();
            status = resultJson.get("status").toString();
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 获取返回信息失败: 订单号%s -> %s", domain.getSerialNo(), sendResult));
        }
        if (code == null) {
            logger.debug(String.format("查询订单 >> 订单(%s)未获取到返回结果集", domain.getSerialNo()));
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回结果集");
            return result;
        }

        if (code.equals("ok")) {
            switch (status) {
                case "SUCCESS":
                    logger.debug(String.format("查询订单 >> 订单 %s -> 订单成功|%s", domain.getSerialNo(), desc));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_SUCCESS);
                    result.setModel(ResultDO.SECOND_MODEL_KEY, "");
                    break;
                case "FAILED":
                    logger.debug(String.format("查询订单 >> 订单 %s -> 订单失败|%s", domain.getSerialNo(), desc));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_FAIL);
                    break;
                case "UNKNOWN":
                    logger.debug(String.format("查询订单 >> 订单 %s -> 订单未确认|%s", domain.getSerialNo(), desc));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                    break;
                case "DEALING":
                    logger.debug(String.format("查询订单 >> 订单 %s -> 订单充值中|%s", domain.getSerialNo(), desc));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                    break;
                default:
                    logger.debug(String.format("查询订单 >> 订单 %s -> 订单未知|%s", domain.getSerialNo(), desc));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                    break;
            }
        }else if (code.equals("0013")){
            logger.debug(String.format("查询订单 >> 订单 %s -> 订单不存在", domain.getSerialNo()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NOT_EXIST);
            result.setModel(ResultDO.FOURTH_MODEL_KEY, 3);
        }else {
            result.setSuccess(false);
        }
        result.setModel(ResultDO.THIRD_MODEL_KEY, desc);
        return result;
    }

    @Override
    public ResultDO queryBalance(ProviderApiDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null || domain.getUserId() == null) {
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("customerNo", domain.getUserId());
        requestParamMap.put("timestamp", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis()));
        String sign = "";
        try {
            sign = MD5Util.md5(requestParamMap, domain.getPrivateKey());
        } catch (Exception e) {
            logger.debug("余额查询 >> " + domain.getUserId() + " 签名加密失败" + e.getMessage());
//            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        requestParamMap.put("sign", sign);

        // 发送请求
        String sendResult = HttpClientUtil.doGet(domain.getBalanceFindUrl(), requestParamMap);
        logger.debug(String.format("余额查询 >> 余额查询返回信息: 供应商账号 %s -> %s", domain.getUserId(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        String code = null;
        String balance = null;
        String desc = "";
        try {
            code = resultJson.get("returnCode").toString();
            if (resultJson.get("balance") != null) {
                StringBuilder buffer = new StringBuilder(resultJson.getString("balance"));
                buffer.insert(buffer.length() - 3, '.');
                balance = buffer.toString();
            }
            if (resultJson.get("returnMsg") != null) {
                desc = resultJson.get("returnMsg").toString();
            }
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 获取返回信息失败: 供应商账号%s -> %s", domain.getUserId(), sendResult));
        }
        if (code == null || !code.equals("ok")) {
//            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询失败 | %s", domain.getUserId(), desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, desc);
            return result;
        }
        result.setModel(ResultDO.FIRST_MODEL_KEY, balance);
//        result.setModel(ResultDO.SECOND_MODEL_KEY, desc);
        return result;
    }

    /**
     * <h2>提交订单返回失败</h2>
     *
     * @param success    是否处理成功
     * @param code       订单状态
     * @param logMessage 失败信息
     * @param message    供货摘要
     * @param entity     返回信息字符串
     * @return FIRST_MODEL_KEY: 订单状态
     * SECOND_MODEL_KEY: 供货摘要
     * THIRD_MODEL_KEY: 上游返回信息字符串
     */
    private ResultDO returnFail(boolean success, int code, String logMessage, String message, String entity) {
        ResultDO result = new ResultSupport();
        result.setSuccess(success);
        result.setModel(ResultDO.FIRST_MODEL_KEY, code);
        result.setModel(ResultDO.SECOND_MODEL_KEY, message);
        result.setModel(ResultDO.THIRD_MODEL_KEY, entity);
        logger.debug(logMessage);
        return result;
    }
}
